/* 
 * File:   BoundingBox.cpp
 * Author: brady
 * 
 * Created on June 20, 2011, 7:15 PM
 */

#include <core/BoundingBox.h>

BoundingBox::BoundingBox() {
    center = Vector3D(0,0,0);
    minCorner = Vector3D(0,0,0);
    maxCorner = Vector3D(0,0,0);
}

BoundingBox::BoundingBox(const Vector3D& min, const Vector3D& max)
{
    this->minCorner = min;
    this->maxCorner = max;
    this->center = (min+max)/2.0;
}

BoundingBox::BoundingBox(const BoundingBox& orig) {
    this->minCorner = orig.minCorner;
    this->maxCorner = orig.maxCorner;
    this->center = orig.center;
}

BoundingBox::~BoundingBox() {
}

void BoundingBox::pad(double amount){
    minCorner -= Vector3D(amount, amount, amount);
    maxCorner += Vector3D(amount, amount, amount);
}

void BoundingBox::setMin(const Vector3D& v){
    this->minCorner = v;
    this->center = (minCorner + maxCorner)/2.0;
}

const Vector3D& BoundingBox::getMin() const {
    return this->minCorner;
}

void BoundingBox::setMax(const Vector3D& v){
    this->maxCorner = v;
    this->center = (minCorner + maxCorner)/2.0;
}

const Vector3D& BoundingBox::getMax() const {
    return this->maxCorner;
}

const Vector3D& BoundingBox::getCenter() const{
    return center;
}

BoundingBox BoundingBox::encompass(const BoundingBox& childBoundingBox){
    Vector3D newMin;
    Vector3D newMax;
    newMin[0] = min(minCorner[0], childBoundingBox.getMin()[0]);
    newMin[1] = min(minCorner[1], childBoundingBox.getMin()[1]);
    newMin[2] = min(minCorner[2], childBoundingBox.getMin()[2]);
    newMax[0] = max(maxCorner[0], childBoundingBox.maxCorner[0]);
    newMax[1] = max(maxCorner[1], childBoundingBox.maxCorner[1]);
    newMax[2] = max(maxCorner[2], childBoundingBox.maxCorner[2]);
    return BoundingBox(newMin, newMax);
}

bool BoundingBox::testIntersect(IntersectionParams& ip, double *tVal_enter, double* tVal_exit) const{
    const Vector3D& Orig = ip.getRay().getOrigin();
    const Vector3D& Dir = ip.getRay().getDirection();
    double origX, origY, origZ, dirX, dirY, dirZ;
    double t_enter, t_exit;
    double t_enter_x, t_enter_y, t_enter_z, t_exit_x, t_exit_y, t_exit_z;
    dirX = Dir[0];
    dirY = Dir[1];
    dirZ = Dir[2];
    origX = Orig[0];
    origY = Orig[1];
    origZ = Orig[2];
   
    if(dirX > 0.0)
    {
        t_enter_x = (minCorner[0] - origX)/dirX;
        t_exit_x = (maxCorner[0] - origX)/dirX;
    }
    else if (dirX < 0.0)
    {
        t_enter_x = (maxCorner[0] - origX) / dirX;
        t_exit_x = (minCorner[0] - origX) / dirX;
    }
    else
    {
        t_enter_x = DBL_MIN;
        t_exit_x = DBL_MAX;
    }
    if(dirY > 0.0)
    {
        t_enter_y = (minCorner[1] - origY)/dirY;
        t_exit_y = (maxCorner[1] - origY)/dirY;
    }
    else if (dirY < 0.0)
    {
        t_enter_y = (maxCorner[1] - origY)/dirY;
        t_exit_y = (minCorner[1] - origY)/dirY;
    }
    else
    {
        t_enter_y = DBL_MIN;
        t_exit_y = DBL_MAX;
    }

   if(dirZ > 0.0)
    {
        t_enter_z = (minCorner[2] - origZ)/dirZ;
        t_exit_z = (maxCorner[2] - origZ)/dirZ;
    }
    else if (dirZ < 0.0)
    {
        t_enter_z = (maxCorner[2] - origZ)/dirZ;
        t_exit_z = (minCorner[2] - origZ)/dirZ;
    }
    else
    {
        t_enter_z = DBL_MIN;
        t_exit_z = DBL_MAX;
    }
    
    t_enter = max(max(t_enter_x, t_enter_y), t_enter_z);
    t_exit = min(min(t_exit_x, t_exit_y), t_exit_z);

    if(t_enter < t_exit){
        //hit

        if(tVal_enter != NULL) *tVal_enter = t_enter;
        if(tVal_exit != NULL)*tVal_exit = t_exit;
        return true;
    }
    else{
        //miss
        tVal_enter = NULL;
        tVal_exit = NULL;
        return false;
    }
}

bool BoundingBox::contains(const Vector3D& point){
    return (point[0] > minCorner[0] && point[0] < maxCorner[0] &&
            point[1] > minCorner[1] && point[1] < maxCorner[1] &&
            point[2] > minCorner[2] && point[2] < maxCorner[2]);
}

bool BoundingBox::overlaps(const BoundingBox& box) const {
    return (minCorner <= box.maxCorner) && (box.minCorner <= maxCorner);
}